package com.boolib.aop.util;

import lombok.extern.slf4j.Slf4j;
import org.aopalliance.aop.Advice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.GenericWebApplicationContext;

import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class SpringUtil implements ApplicationContextAware , BeanPostProcessor {


    private static volatile  boolean isProxy = false;

    private static volatile  String beanProxyName = "";

    private static volatile Advice adviceProxy;

    private static GenericWebApplicationContext context;

    public static <T>  T getBean(String name){
        return (T) context.getBean(name);
    }


    /**
     * 移除bean
     * @param name
     */
    public static void removeBean(String name){
        context.removeBeanDefinition(name);
    }


    /**
     * 还原代理bean
     * @param name
     */
    public static void recoverProxyBean(String name){
        if(isProxy) {
            replaceProxyBean(false, name, null);
        }
    }

    /**
     * 刷新bean
     * @param name
     */
    public static void replaceBean(String name){

        BeanDefinition beanDefinition = context.getBeanDefinition(name);

        context.removeBeanDefinition(name);

        context.registerBeanDefinition(name,beanDefinition);
    }


    /**
     * 替换代理bean
     * @param name
     * @param advice
     */
    public static void replaceProxyBean(String name,Advice advice){
        replaceProxyBean(true,name,advice);
    }


    /**
     * 替换代理bean
     * @param isProxyFlag
     * @param name
     * @param advice
     */
    public static void replaceProxyBean(boolean isProxyFlag ,String name,Advice advice){

        fillVariable(isProxyFlag,name,advice);

        BeanDefinition beanDefinition = context.getBeanDefinition(name);

        context.removeBeanDefinition(name);

        context.registerBeanDefinition(name,beanDefinition);

    }


    /**
     * 填充数据
     * @param isProxyFlag
     * @param name
     * @param advice
     */
    private static void fillVariable(boolean isProxyFlag,String name,Advice advice){
        isProxy = isProxyFlag;
        beanProxyName = name;
        adviceProxy = advice;
    }


    /**
     * bean初始化后执行，在替换bean的时候不会直接触发，当有调用的时候才会触发。
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if(beanName.equalsIgnoreCase(beanProxyName) && isProxy){
            log.info("bean:{},替换成代理bean",beanName);
            return proxy(bean,adviceProxy);
        }

        return  bean;
    }

    @Override
    public void  setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = (GenericWebApplicationContext) applicationContext;
    }


    /**
     * 设置代理
     * @param target
     * @param advice
     * @return
     */
    public static Object proxy(Object target,Advice advice){

        ProxyFactory proxyFactory = new ProxyFactory();

        proxyFactory.addAdvice(advice);

        proxyFactory.setTarget(target);

        return proxyFactory.getProxy();

    }

}
